import requests
from ..constant import AssertType, DataType, AssertRelation
from .extractor import Extract
from requests import exceptions
import traceback


# 执行单一用例
class RequestProcess:

    def __init__(self, req_info, time_out=10):
        self.req_info: dict = req_info
        self.timeout = time_out
        self.response = None
        self.__errors = []

    def sync_req(self):
        self.req_info.setdefault('timeout', self.timeout)
        try:
            r = requests.request(**self.req_info)
            self.response = r
        except Exception as e:
            self.__errors.append(traceback.format_exc())

    @property
    def has_err(self):
        return True if self.__errors else False

    def get_errs(self):
        return self.__errors

    def get_response(self):
        return self.response


class AssertProcess:
    def __init__(self, assert_info: dict, response: requests.models.Response):

        self.response = response

        self.AssertStatusCode: list = assert_info['assert_status']
        self.AssertHeaders: list = assert_info['assert_headers']
        self.AssertJson: list = assert_info['assert_json']

        self.__assert_error = []  # 断言失败
        self.__assert_pass = []  # 断言成功
        self.__errors = []  # 程序错误

        self.DT = DataType()
        self.AT = AssertType()
        self.AR = AssertRelation()

        self.extractor = Extract(response)

    def _add_assert_result(self, result: bool, type, expression,
                           actual_data, actual_type, relation, expect_data, expect_type):
        _r = {
            'expression': str(expression),
            'actual_data': str(actual_data) if actual_data else actual_data,
            'actual_type': str(actual_type) if actual_type else actual_type,
            'relation': str(relation),
            'expect_data': str(expect_data),
            'expect_type': str(expect_type),
        }
        if result:
            self.__assert_pass.append({
                'id': type[0],
                'name': type[1],
                'result': _r
            })
        else:
            self.__assert_error.append({
                'id': type[0],
                'name': type[1],
                'result': _r
            })

    def _base_assert(self, assert_type: str, info: dict, extractor):
        '''
        assert_obj:断言对象
        info:self.assert_BodyJson 中一条元素
        extractor:Extract().json()...
        '''
        # 表达式，期望值类型，运算关系，期望值
        try:
            expr, expect_type, relation, expect_str = info['expr'], info['type'], info['relation'], info['expectation']
            actual = extractor(expr)  # 实际值
            expect = self.DT.transform_type(expect_type, expect_str)  # 期望值
            assert_result = self.AR.calculate(actual, relation, expect)  # 实际值 （运算） 期望值
            if assert_result:
                self._add_assert_result(
                    True, assert_type, expr,
                    actual, self.DT.get_type(actual),
                    self.AR.get_id_or_name(relation),
                    expect_str, self.DT.get_id_or_name(expect_type)
                )
            else:
                self._add_assert_result(
                    False, assert_type, expr,
                    actual, self.DT.get_type(actual),
                    self.AR.get_id_or_name(relation),
                    expect_str, self.DT.get_id_or_name(expect_type)
                )
        except Exception as e:
            self.__errors.append(e.args)

    def assert_status_code(self):
        # 断言响应状态码
        for info in self.AssertStatusCode:
            self._base_assert(self.AT.StatusCode, info, self.extractor.status_code)

    def assert_res_headers(self):
        # 断言响应头
        for info in self.AssertHeaders:
            self._base_assert(self.AT.ResHeaders, info, self.extractor.headers)

    def assert_json(self):
        # 断言json响应体
        for info in self.AssertJson:
            self._base_assert(self.AT.JsonBody, info, self.extractor.json)

    @property
    def has_err(self):
        return True if self.__errors else False

    @property
    def has_assert_err(self):
        return True if self.__assert_error else False

    def get_errs(self):
        return self.__errors

    def get_assert_error(self):
        return self.__assert_error

    def get_assert_pass(self):
        return self.__assert_pass

    def exec_assert(self):
        self.assert_status_code()
        self.assert_res_headers()
        self.assert_json()
